package com.mdp.workflow.biz.ru.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mdp.core.api.CacheHKVService;
import com.mdp.core.entity.Result;
import com.mdp.core.query.QueryTools;
import com.mdp.core.service.BaseService;
import com.mdp.core.utils.BaseUtils;
import com.mdp.core.utils.ObjectTools;
import com.mdp.micro.client.CallBizService;
import com.mdp.safe.client.entity.User;
import com.mdp.safe.client.utils.LoginUtils;
import com.mdp.workflow.api.*;
import com.mdp.workflow.biz.re.entity.Procdef;
import com.mdp.workflow.biz.re.vo.ModelAttrsVo;
import com.mdp.workflow.biz.ru.entity.Execution;
import com.mdp.workflow.biz.ru.entity.ProcinstExpand;
import com.mdp.workflow.biz.ru.mapper.ExecutionMapper;
import com.mdp.workflow.biz.ru.vo.ProcStartVo;
import com.mdp.workflow.bus.candidate.strategy.StartUserSelectStrategy;
import com.mdp.workflow.client.service.WfSysUserService;
import com.mdp.workflow.comm.dict.CandidateStrategy;
import com.mdp.workflow.comm.dict.ProcinstConstants;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.IdentityService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.xml.stream.events.StartElement;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.mdp.core.utils.CollectionUtils.convertMap;
import static com.mdp.workflow.api.FlowModelService.START_PROC_PRE_CACHE_KEY;

/**
 * 父类已经支持增删改查操作,因此,即使本类什么也不写,也已经可以满足一般的增删改查操作了.<br> 
 * 组织 com.mdp  顶级模块 workflow 大模块 ru 小模块 <br>
 * 实体 Execution 表 act_ru_execution 当前主键(包括多主键): id_; 
 ***/
@Service("mdp.workflow.ru.ExecutionServiceImpl")
public class FlowExecutionServiceImpl extends BaseService<ExecutionMapper,Execution> implements FlowExecutionService {
	static Logger logger =LoggerFactory.getLogger(FlowExecutionServiceImpl.class);

	@Autowired
	public RuntimeService runtimeService;
	@Autowired
	IdentityService identityService;

	@Autowired
	CallBizService callBizService;


	@Autowired
	ProcinstNodeInfoService procinstNodeInfoService;
 

	@Autowired
	public FlowFormDataService procinstFormDataService;

	@Autowired
	TaskService taskService;

	@Autowired
	WfSysUserService sysUserService;

	@Value(value = "${mdp.form.data-approva-uri:/lcode/formData/processApprova}")
	String formDataApprovaUri="";

	@Autowired
	FlowMsgService pushMsgService;


	@Autowired
	FlowProcdefService flowProcdefService;

	@Autowired
	FlowModelService flowModelService;


	@Autowired
	RepositoryService repositoryService;

	@Autowired
	CacheHKVService cacheHKVService;

	@Autowired
	ProcinstExpandService procinstExpandService;

	@Autowired
	WfSysUserService wfSysUserService;

	/**
	 * 自定义查询，支持多表关联
	 * @param page 分页条件
	 * @param ew 一定要，并且必须加@Param("ew")注解
	 * @param ext 如果xml中需要根据某些值进行特殊处理，可以通过这个进行传递，非必须，注解也可以不加
	 * @return
	 */
	public List<Map<String,Object>> selectListMapByWhere(IPage page, QueryWrapper<Execution> ew, Map<String,Object> ext){
		List<Map<String,Object>> datas = baseMapper.selectListMapByWhere(page,ew,ext);
		if(datas==null||datas.size()==0) {
			return datas;
		}
		Set<String> set=new HashSet<>();
		for (Map<String, Object> data : datas) {
			set.add((String) data.get("assignee"));
		}
		Map<String,String> umap=!set.isEmpty()? convertMap(wfSysUserService.selectSimpleUsersByUserids(set),o->o.get("userid"),o->o.get("username")):null;
		if(umap==null || umap.size()==0){
			return datas;
		}
		for (Map<String, Object> data : datas) {
			String id= (String) data.get("assignee");
			data.put("assigneeName",umap.get(id));
		}
		return datas;

	}

	/**
	 * 流程启动时，需要下放流程的参数给前端，用于做页面展示或者控制
	 * @param procDefId
	 * @return
	 * {
	 *     strategy:  字符串，候选人分配策略
	 *     mainExtAttrs: {name:value} ,map类型，流程属性配置
	 *     data: ProcessDefinition, 流程定义信息
	 * }
	 */
	public Result startPre(String procDefId){
		String key=procDefId.split(":")[0];
		Result result= (Result) cacheHKVService.get(START_PROC_PRE_CACHE_KEY,key);
		if(result==null){
			result=Result.ok();
		}else{
			return result;
		}
		ProcessDefinition definition=flowProcdefService.getProcessDefinition(procDefId);
		Procdef procdef=new Procdef();
		BeanUtils.copyProperties(definition,procdef);
		result.setData(procdef);
		//1.拉取审批人分配策略
		// 1. 获得发起人自选审批人的 UserTask 列表
		BpmnModel bpmnModel = flowProcdefService.getProcessDefinitionBpmnModel(definition.getId());
		List<UserTask> userTaskList = StartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
		if (!CollUtil.isEmpty(userTaskList)) {
			userTaskList.removeIf(k->!(k instanceof StartElement));
			result.put("strategy", CandidateStrategy.START_USER_SELECT);
		}

		//2.拉取主办人+监控人+正文+附件等流程级别的扩展属性
		Map<String,Object> mainExtAttrs=new HashMap<>();
		bpmnModel.getMainProcess().getExtensionElements().values().forEach(k->{
			for (ExtensionElement e : k) {
				for (List<ExtensionAttribute> ea : e.getAttributes().values()) {
					for (ExtensionAttribute extensionAttribute : ea) {
						mainExtAttrs.put(extensionAttribute.getName(),extensionAttribute.getValue());
					}
				}
			}
		});
		for (List<ExtensionAttribute> ea : bpmnModel.getMainProcess().getAttributes().values()) {
			for (ExtensionAttribute extensionAttribute : ea) {
				mainExtAttrs.put(extensionAttribute.getName(),extensionAttribute.getValue());
			}
		}
		result.put("mainExtAttrs",mainExtAttrs );

		// 3 拉取流程图的xml，用于展示流程图
 		Model model=flowModelService.getModelByKey(definition.getKey());
		 byte[] bpmnXml=flowModelService.getModelBpmnXML(model.getId());
		 result.put("bpmnXml",new String(bpmnXml));
		 cacheHKVService.put(START_PROC_PRE_CACHE_KEY,key,result);
		cacheHKVService.expire(START_PROC_PRE_CACHE_KEY,1, TimeUnit.DAYS	);
		return result;
	}



	/** 请在此类添加自定义函数 
	 * @return */
	@Transactional
	public Result  start(ProcStartVo procStartVo) {

		ModelAttrsVo modelAttrsVo=BaseUtils.fromMap(procStartVo.getFlowVars(),ModelAttrsVo.class);
		User user= LoginUtils.getCurrentUserInfo();
		Map<String,Object> fds=procStartVo.getFormData();
		Map<String,Object> procInstVars=this.procInstVarsHandle(fds,procStartVo);
		ProcessInstance processInstance;
		List<String> nextAssignees=procStartVo.getNextAssignees();
		Map<String,Object> localVars=this.localVarsHandle( fds,procStartVo.getFlowVars(),null);

		/**
		 * 需要事先保存，有可能启动过程中，第一个节点就有指派策略用到这里的数据
		 */
		if(procStartVo.getNodeInfos()!=null && procStartVo.getNodeInfos().size()>0){
			 procinstNodeInfoService.saveBatch(procStartVo.getNodeInfos());
		} 
		if(nextAssignees!=null &&nextAssignees.size()>0){
			localVars.put("nextAssignees",nextAssignees);
		}
		try {
			identityService.setAuthenticatedUserId(user.getUserid());
			identityService.setUserInfo(user.getUserid(), "username", user.getUsername());
			identityService.setUserInfo(user.getUserid(), "branchId", user.getBranchId());
			processInstance=runtimeService.createProcessInstanceBuilder().
					processDefinitionId(procStartVo.getProcDefId()).
					businessKey(procStartVo.getBizKey()).
					name(procStartVo.getMainTitle()).
					transientVariables(localVars).
					variables(procInstVars).
					tenantId(user.getBranchId()).
					start();

		}finally {
			identityService.setAuthenticatedUserId(null);
		}
		saveProcinstExpand(procStartVo,processInstance.getId(),user);
		return Result.ok().setData(processInstance);

	}

	public ProcinstExpand saveProcinstExpand(ProcStartVo procStartVo,String procInstId,User user){
		ProcinstExpand procinstExpand=new ProcinstExpand();
		BeanUtils.copyProperties(procStartVo,procinstExpand);
		procinstExpand.setStartDate(new Date());
		procinstExpand.setUserid(user.getUserid());
		procinstExpand.setDel("0");
		procinstExpand.setBranchId(user.getBranchId());
		procinstExpand.setDeptid(user.getDeptid());
		procinstExpand.setStartUsername(user.getUsername());
		procinstExpand.setProcInstId(procInstId);
		procinstExpandService.save(procinstExpand);
		return procinstExpand;
	}

	/**
	 * 激活流程
	 * @param processInstanceId
	 * @return
	 */
	public Result  activateProcessInstance(String processInstanceId){

		runtimeService.activateProcessInstanceById(processInstanceId);
		return Result.ok();
	}
	/**
	 * 挂起流程
	 * @param processInstanceId
	 * @return
	 */
	public Result  suspendProcessInstance(String processInstanceId){

		runtimeService.suspendProcessInstanceById(processInstanceId);
		return Result.ok();
	}


	/**
	 * 将一些外部变量存入工作流引擎 deptid,userid,sponsors
	 * @param formData
	 * @return
	 */
	public Map<String,Object> procInstVarsHandle(Map<String,Object> formData,ProcStartVo vo){
		User user=LoginUtils.getCurrentUserInfo();
		Map<String,Object> flowVars=vo.getFlowVars();
 		Map<String,Object> procInstVars=new HashMap<String,Object>();
		if(formData!=null && !formData.isEmpty() ){
 			procInstVars.put(ProcinstConstants.FORM_DATA,formData);
		}
		if(flowVars!=null) {
			procInstVars.putAll(flowVars);
		}
		if(ObjectTools.isNotEmpty(vo.getRestUrl())){
			procInstVars.put(ProcinstConstants.REST_URL,vo.getRestUrl());
		}
		if(ObjectTools.isNotEmpty(vo.getBizUrl())){
			procInstVars.put(ProcinstConstants.BIZ_URL,vo.getBizUrl());
		}
		if(ObjectTools.isNotEmpty(vo.getBizKey())){
			procInstVars.put(ProcinstConstants.BIZ_KEY,vo.getBizKey());
		}
		procInstVars.put(ProcinstConstants.START_DEPTID, user.getDeptid());
		procInstVars.put(ProcinstConstants.START_USERID, user.getUserid());
		procInstVars.put(ProcinstConstants.START_USERNAME, user.getUsername());
		procInstVars.put(ProcinstConstants.BRANCH_ID,user.getBranchId());

		return procInstVars;
	}
	/**
	 * 将一些外部变量存入工作流引擎 本地变量中，不会持久化的变量
	 * 方便事件、或者其它判断临时使用，避免多次查询，搜素
	 * @param
	 * @param formData
	 * @return
	 */
	public Map<String,Object> localVarsHandle( Map<String,Object> formData,Map<String,Object> flowVars,Map<String,Object> localVars){
		User user = LoginUtils.getCurrentUserInfo();
		localVars=localVars==null?new HashMap<String,Object>():localVars;
		if(flowVars!=null) {
			localVars.putAll(flowVars);
		}
		localVars.put(ProcinstConstants.FORM_DATA,formData);
 		localVars.put(ProcinstConstants.START_DEPTID, user.getDeptid());
 		localVars.put(ProcinstConstants.START_USERID, user.getUserid());
		localVars.put(ProcinstConstants.START_USERNAME, user.getUsername());
		localVars.put(ProcinstConstants.BRANCH_ID,user.getBranchId());
		localVars.put(ProcinstConstants.CURR_USERID, user.getUserid());
		localVars.put(ProcinstConstants.CURR_USERNAME, user.getUsername());
		localVars.put(ProcinstConstants.IS_START, "1");
		return localVars;
	}
	public List<Map<String,Object>> listProcinst(Map<String, Object> p) {
		List<Map<String,Object>> datas= this.selectListMapByWhere(null, QueryTools.initQueryWrapper(Execution.class,p),p);
		if(datas==null || datas.size()==0){
			return datas;
		}else{//assignee_,'' assignee_name
			Set<String> set=datas.stream().map(i->(String)i.get("assignee")).collect(Collectors.toSet());
			Map<String,String> usersMap= convertMap(this.sysUserService.selectSimpleUsersByUserids(set),o->o.get("userid"),o->o.get("username"));
			for (Map<String, Object> data : datas) {
				data.put("assigneeName",usersMap.get(data.get("assignee")));
			}
			return datas;
		}

	}


	public Result  delExecution(String processInstanceId,String commentMsg,Map<String,Object> localVars) {
		if(localVars!=null){
			localVars=new HashMap<>();
		}

		localVars.put("commentMsg", commentMsg);
		runtimeService.setVariablesLocal(processInstanceId, localVars);
		runtimeService.deleteProcessInstance(processInstanceId, commentMsg);
		return Result.ok();
	}

}

